package com.passion.common.util;

import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;


/**
 * 类描述：反射工具类
 *
 * @author ZhuYin
 * @since 2025年01月03日
 */
public class ReflectionUtil {

    /** 缓存类的字段、方法和构造函数 */
    private static final Map<Class<?>, Map<String, Field>> fieldCache = new ConcurrentHashMap<>();
    private static final Map<Class<?>, Map<String, Method>> methodCache = new ConcurrentHashMap<>();
    private static final Map<Class<?>, Constructor<?>> constructorCache = new ConcurrentHashMap<>();
    private static final Map<String, MethodHandle> methodHandleCache = new ConcurrentHashMap<>();

    /**
     * 获取指定类的字段。
     *
     * @param clazz     目标类
     * @param fieldName 字段名
     * @return 对应的字段对象
     * @throws NoSuchFieldException 如果找不到指定的字段
     */
    public static Field getField(Class<?> clazz, String fieldName) throws NoSuchFieldException {
        return getFields(clazz).get(fieldName);
    }

    /**
     * 获取指定类及其父类的所有字段，并将其缓存。
     *
     * @param clazz 目标类
     * @return 包含所有字段的映射，键为字段名，值为字段对象
     */
    public static Map<String, Field> getFields(Class<?> clazz) {
        return fieldCache.computeIfAbsent(clazz, key -> {
            Map<String, Field> fields = new HashMap<>();
            Class<?> currentClass = clazz;
            while (currentClass != null) {
                for (Field field : currentClass.getDeclaredFields()) {
                    fields.put(field.getName(), field);
                }
                currentClass = currentClass.getSuperclass();
            }
            return fields;
        });
    }

    /**
     * 设置指定对象的字段值。支持基本类型和包装类型的自动转换。
     *
     * @param target   目标对象
     * @param fieldName 字段名
     * @param value    要设置的值
     * @throws IllegalAccessException 如果无法访问字段
     * @throws NoSuchFieldException   如果找不到指定的字段
     */
    public static void setFieldValue(Object target, String fieldName, Object value) throws IllegalAccessException, NoSuchFieldException {
        Field field = getField(target.getClass(), fieldName);
        field.setAccessible(true);
        field.set(target, convertValue(field.getType(), value));
    }

    /**
     * 获取指定对象的字段值。
     *
     * @param target   目标对象
     * @param fieldName 字段名
     * @return 字段的值
     * @throws IllegalAccessException 如果无法访问字段
     * @throws NoSuchFieldException   如果找不到指定的字段
     */
    public static Object getFieldValue(Object target, String fieldName) throws IllegalAccessException, NoSuchFieldException {
        Field field = getField(target.getClass(), fieldName);
        field.setAccessible(true);
        return field.get(target);
    }

    /**
     * 获取指定类的方法。
     *
     * @param clazz           目标类
     * @param methodName      方法名
     * @param parameterTypes  方法参数类型
     * @return 对应的方法对象
     * @throws NoSuchMethodException 如果找不到指定的方法
     */
    public static Method getMethod(Class<?> clazz, String methodName, Class<?>... parameterTypes) throws NoSuchMethodException {
        return getMethods(clazz).computeIfAbsent(methodName + Arrays.toString(parameterTypes), key -> {
            try {
                return clazz.getDeclaredMethod(methodName, parameterTypes);
            } catch (NoSuchMethodException e) {
                throw new RuntimeException(e);
            }
        });
    }

    /**
     * 获取指定类及其父类的所有方法，并将其缓存。
     *
     * @param clazz 目标类
     * @return 包含所有方法的映射，键为方法签名（方法名+参数类型），值为方法对象
     */
    public static Map<String, Method> getMethods(Class<?> clazz) {
        return methodCache.computeIfAbsent(clazz, key -> {
            Map<String, Method> methods = new HashMap<>();
            Class<?> currentClass = clazz;
            while (currentClass != null) {
                for (Method method : currentClass.getDeclaredMethods()) {
                    methods.put(method.getName() + Arrays.toString(method.getParameterTypes()), method);
                }
                currentClass = currentClass.getSuperclass();
            }
            return methods;
        });
    }

    /**
     * 调用指定对象的方法。支持传递参数，并处理基本类型和包装类型的自动转换。
     *
     * @param target  目标对象
     * @param methodName 方法名
     * @param args    方法参数
     * @return 方法的返回值
     * @throws InvocationTargetException 如果方法调用过程中抛出异常
     * @throws IllegalAccessException    如果无法访问方法
     * @throws NoSuchMethodException    如果找不到指定的方法
     */
    public static Object invokeMethod(Object target, String methodName, Object... args) throws InvocationTargetException, IllegalAccessException, NoSuchMethodException {
        Class<?>[] parameterTypes = getParameterTypes(args);
        Method method = getMethod(target.getClass(), methodName, parameterTypes);
        method.setAccessible(true);
        return method.invoke(target, args);
    }

    /**
     * 获取指定类的构造函数。
     *
     * @param clazz           目标类
     * @param parameterTypes  构造函数参数类型
     * @return 对应的构造函数对象
     * @throws NoSuchMethodException 如果找不到指定的构造函数
     */
    public static Constructor<?> getConstructor(Class<?> clazz, Class<?>... parameterTypes) throws NoSuchMethodException {
        return constructorCache.computeIfAbsent(clazz, key -> {
            try {
                return clazz.getDeclaredConstructor(parameterTypes);
            } catch (NoSuchMethodException e) {
                throw new RuntimeException(e);
            }
        });
    }

    /**
     * 创建指定类的实例。支持传递参数，并处理基本类型和包装类型的自动转换。
     *
     * @param <T>             目标类的类型
     * @param clazz           目标类
     * @param args            构造函数参数
     * @return 新创建的对象实例
     * @throws InstantiationException 如果无法实例化类
     * @throws IllegalAccessException 如果无法访问构造函数
     * @throws InvocationTargetException 如果构造函数调用过程中抛出异常
     * @throws NoSuchMethodException    如果找不到指定的构造函数
     */
    public static <T> T newInstance(Class<T> clazz, Object... args) throws InstantiationException, IllegalAccessException, InvocationTargetException, NoSuchMethodException {
        Class<?>[] parameterTypes = getParameterTypes(args);
        Constructor<?> constructor = getConstructor(clazz, parameterTypes);
        constructor.setAccessible(true);
        return (T) constructor.newInstance(args);
    }

    /**
     * 使用 {@link MethodHandle} 调用指定对象的方法，以提高性能。
     *
     * @param target  目标对象
     * @param methodName 方法名
     * @param args    方法参数
     * @return 方法的返回值
     * @throws Throwable 如果方法调用过程中抛出异常
     */
    public static Object invokeMethodWithHandle(Object target, String methodName, Object... args) throws Throwable {
        String key = target.getClass().getName() + ":" + methodName + Arrays.toString(getParameterTypes(args));
        MethodHandle methodHandle = methodHandleCache.computeIfAbsent(key, k -> {
            try {
                Method method = getMethod(target.getClass(), methodName, getParameterTypes(args));
                method.setAccessible(true);
                return MethodHandles.lookup().unreflect(method);
            } catch (IllegalAccessException | NoSuchMethodException e) {
                throw new RuntimeException(e);
            }
        });
        return methodHandle.bindTo(target).invokeWithArguments(args);
    }

    /**
     * 将传入的值转换为目标字段的类型。支持基本类型和包装类型的自动转换。
     *
     * @param fieldType 目标字段的类型
     * @param value     要转换的值
     * @return 转换后的值
     */
    private static Object convertValue(Class<?> fieldType, Object value) {
        if (value == null) {
            return null;
        }
        if (fieldType.isPrimitive()) {
            if (fieldType == boolean.class) {
                return Boolean.parseBoolean(value.toString());
            } else if (fieldType == byte.class) {
                return Byte.parseByte(value.toString());
            } else if (fieldType == short.class) {
                return Short.parseShort(value.toString());
            } else if (fieldType == int.class) {
                return Integer.parseInt(value.toString());
            } else if (fieldType == long.class) {
                return Long.parseLong(value.toString());
            } else if (fieldType == float.class) {
                return Float.parseFloat(value.toString());
            } else if (fieldType == double.class) {
                return Double.parseDouble(value.toString());
            } else if (fieldType == char.class && value instanceof String) {
                return ((String) value).charAt(0);
            }
        }
        return value;
    }

    /**
     * 根据传入的参数对象数组，返回相应的参数类型数组。如果参数为 null，则使用 Void.TYPE 作为占位符。
     *
     * @param args 参数对象数组
     * @return 参数类型数组
     */
    private static Class<?>[] getParameterTypes(Object[] args) {
        if (args == null) {
            return new Class<?>[0];
        }
        Class<?>[] parameterTypes = new Class<?>[args.length];
        for (int i = 0; i < args.length; i++) {
            parameterTypes[i] = args[i] != null ? args[i].getClass() : Void.TYPE;
        }
        return parameterTypes;
    }
}